package ldk.apps.adbconnections

import com.google.gson.Gson
import ldk.apps.adbconnections.beans.Result
import java.io.*
import java.net.ServerSocket
import java.net.Socket
import java.nio.ByteBuffer

/**
 * socket服务器
 * Created by ldk on 2/4/18.
 */
class MainServer {
    companion object {
        val PORT = 9318      // 端口号
        val DEVIDER = 0x18   // Action之间的分隔符
        val END = 0x00       // 终止服务操作
        val HEART = 0x01     // 心跳动作， 保持socket活动
        val HEART_TIME: Long = 10*1000  // 心跳间隔时间
    }

    private var socket: Socket? = null
    private var socketInputStream: BufferedInputStream? = null
    private var socketOutputStream: OutputStream? = null
    @Volatile
    private var serverSocket: ServerSocket?
    private val mainThread: Thread
    private var lastTime: Long = 0
    private val gson = Gson()

    // 回调监听
    var handleOnAccept: (()-> Unit)? = null
    var handleOnAction: ((Int, ByteArray?) -> Unit)? = null
    var shouldAccept: (()-> Boolean)? = null
    var handleLog: ((String)-> Unit)? = null


    init{
        serverSocket = ServerSocket(PORT)
        mainThread = Thread{
            readStream()
        }
        mainThread.start()
    }

    private fun readStream() {
        while (true) {
            try {
                socket = serverSocket!!.accept()
                socketInputStream = BufferedInputStream(socket!!.getInputStream())
                socketOutputStream = BufferedOutputStream(socket!!.getOutputStream())
                handleOnAccept?.invoke()
                handleStream()
            } catch (e: IOException) {
                handleLog?.invoke("出现异常: $e")
                socket?.close()
                socketInputStream?.close()
                socketOutputStream?.close()
            }
            socket = null
            socketInputStream = null
            socketOutputStream = null
            if (shouldAccept?.invoke() != true
                    || serverSocket == null) {
                closeServer()
                break
            }
        }
    }

    fun writeResponse(contentBytes: ByteArray){
        socketOutputStream?.write(DEVIDER)
        val length = contentBytes.size.toShort()
        val buffer = ByteBuffer.allocate(2)
        buffer.putShort(length)
        socketOutputStream?.write(buffer.array())
        socketOutputStream?.write(contentBytes)
        socketOutputStream?.flush()
    }

    fun writeResponse(result: Result<*>){
        checkThread()
        if (serverSocket != null){
            val response = gson.toJson(result)
            handleLog?.invoke("返回响应值: $response")
            val contentBytes = response.toByteArray(Charsets.UTF_8)
            writeResponse(contentBytes)
        }
    }

    private fun checkThread() {
        if (Thread.currentThread() != mainThread)
            throw IllegalStateException("writeResponse必须在handleOnAction中调用")
    }

    fun closeServer(){
        serverSocket?.close()
        serverSocket = null
        handleLog?.invoke("关闭服务器")
    }

    fun startHeart(){
        checkThread()
        try {
            while (socketInputStream != null){
                Thread.sleep(HEART_TIME)
                if (socketInputStream!!.available() > 0){
                    // 心跳收到
                    val heartAction = socketInputStream!!.read()
                    if (heartAction == HEART){
                        lastTime = System.currentTimeMillis()
                        socketOutputStream?.write(HEART)
                    } else
                        throw IllegalStateException("action is not heart")
                }else{
                    val deltaTime = System.currentTimeMillis() - lastTime
                    if (deltaTime / HEART_TIME > 3){
                        throw IllegalStateException("心跳超过三次未上报")
                    }
                    socketOutputStream?.write(HEART)
                }
            }
        }catch (e: Exception){
            when (e){
                is InterruptedException, is IOException -> {
                    handleLog?.invoke("心跳正常异常: $e")
                }
                else-> throw e
            }
        }
    }

    fun stopHeart(){
        mainThread.interrupt()
    }

    private fun handleStream(){
        while (socketInputStream != null){
            val inputStream = socketInputStream!!
            while (true){
                if (DEVIDER == inputStream.read()){
                    break
                }else if (-1 == inputStream.read()){
                    return
                }
            }

            val action = inputStream.read()
            if (action == END) return
            if (action == HEART) continue
            val lengthByteArray = ByteArray(2)
            inputStream.read(lengthByteArray)
            val length = ByteBuffer.wrap(lengthByteArray).getShort().toInt()
            var contentArray: ByteArray? = null
            if (length != 0){
                val byteBuffer = ByteBuffer.allocate(length)
                contentArray = byteBuffer.array()
                inputStream.read(contentArray)
            }
            lastTime = System.currentTimeMillis()
            handleOnAction?.invoke(action, contentArray)
        }
    }
}